const getDbName = require('../async/getDbName')
const getCloud = require('./getCloud')

// jql处理工具
const jqlObj = {
	// 单个值处理
	handleValue(value) {
		if (typeof(value) === 'string') {
			return `'${value}'`
		}
		return value
	},
	// 多个值处理
	handleArray(array) {
		for (let i = 0; i < array.length; i++) {
			array[i] = `(${array[i]})`
		}
	},
	// 不存在的表示
	unexist(key) {
		return [
			`${key}==""`,
			`${key}==null`,
			`${key}==[]`
		]
	},
}

/**
 * 通过经纬度字符串得到地理位置
 * 纬度在前，经度在后
 */
const getGeoPoint = (position, db) => {
	if (position) {
		var lng, lat
		if (typeof(position) === 'string') {
			const arr = position.split(',')
			lng = parseFloat(nospaceStr(arr[1]))
			lat = parseFloat(nospaceStr(arr[0]))
		} else if (Array.isArray(position) && position.length === 2) {
			lng = position[0]
			lat = position[1]
		}
		if (-180 > lng && 180 < lng && -90 > lat && 90 < lat) {
			// 不满足经纬度要求
		} else {
			return new db.Geo.Point(lng, lat)
		}
	}
	return null
}

/**
 * 查询参数处理
 * 
doc 表示id的字符串。优先级最高

 * 查询参数。对象或者字符串
 * 如果是对象，key值一定是数据库字段名，每个字段可以有如下查询规则
 *  exist：查询该对象的值存在或者不存在
 *  search：该字段正则匹配
 *  select：该字段包含语法
 * 	defuse：该字段的排除语法，与select相反
 * 	daterange：按时间范围筛选。该字段本身就是时间范围，要筛选该时间范围是否在要提供的时间范围内
 * 	date：按时间筛选
 * 	range：按范围筛选
 * 如果是字符串，除了 'all'之外， 表示jql语法
where

jqlAnd 字符串。如果有值，表示一定是jql查询。where对象里面的内容将会用and与之拼接。方便增加与的参数

jqlOr 字符串。如果有值，表示一定是jql查询。where对象里面的内容将会用or与之拼接。方便增加或的参数

field 字符串或者字符串数组，或者对象。返回字段要求

orderBy = 'timestamp desc' 字符串。排序要求。默认按创建时间降序

pagination 对象。查询时的页码控制器。需包含起始页pageIndex: 0；每页数量pageSize: 10。将会查询对应的总数

getTree 布尔值或对象。获取子节点

getTreePath 对象。获取父节点

parentKey 字符串。指定树形结构数据的parentKey

remove 数值。0：不查一般性删除的内容，这是正常查询；1：这是查询完整的全部；2：这是查一般性删除的内容

getCount = false 布尔值。是否只查询满足条件的总数。返回data就是数量值

 * 对象
 * 关键字查询
 * 	keys：要查询的字段。可以是数组或者带逗号的字符串；
 * 	word:要查询的关键字
keysword

 * 对象或数组（对象，会转化为一维元素的数组）
 * 需要查询的关联表信息。数组，每一个元素信息如下：
 * 	space：对应表的云服务空间。不传，默认为当前云服务空间
 * 	name：对应表的名称。系统字段foreignKey的前部分。至少每一层不能重复，如果有多个，在foreignKey中添加字段映射即可
 *  foreignKey：对象。key表示当前字段名，value表示对应表的字段名。
 * 	field：返回字段要求
 * 	children：多重联表的要求，结构同foreign
foreign

 * 对象
 * 地理位置查询
 * 	key, // 对应计算数据库的字符串
 *  position, // 必填。字符串。当前要计算的地点的经纬度值。纬度在前，经度在后
 *  includeLocs, // 字符串。列出要用于距离计算的字段，如果记录中有多个字段都是地理位置时有用
 *  maxDistance, // 数值。距离最大值
 *  minDistance, // 数值。距离最小值
 *  distanceField, // 必填。字符串。存放距离的输出字段名，可以用点表示法表示一个嵌套字段
 *  distanceMultiplier // 数值。返回时在距离上乘以该数字
GeoPoint

	另有其它非查询类的参数
	data, // 比如要新增修改的内容。一般是对象，新增时可以是数组。新增的内容，尽量不要带_id；修改的内容，务必不能带_id
	isDelete, // 是否真的删除。默认false
	isReturn, // 是否返回完整的最新数据
	 * 新增时不满足条件的处理类型
	 * 	requiredType = -1, // 判断必要性后的类型，-1：忽略必要性判断；0：不新增；1：仍然新增（就会导致必要的数据不完整）
	 * 	onlyType = -1 , // 判断唯一性后的类型，-1：忽略唯一性判断；0：不新增；1：修改原数据（只修改本次已有的）；2：覆盖原数据（会保留_id）；3：覆盖原数据（不保留_id）
	addType

 * getBack：布尔值或方法。查询类型时需要回调方法，将一些在查询前中后需要处理的内容回调出去；不需要回调时直接传true，传了true一定会查到内容
 */
module.exports = (cloudObj, params = {}, config = {}, isApp = false, getBack) => {
	var {cloud, name, space} = cloudObj
	if (!cloud) {
		var obj = getCloud({space, name, type: 'get'}, isApp)
		if (obj) {
			cloudObj.cloud = cloud = obj.cloud
		}
	}
	var {showConsole, isTest, timeout = 10000} = config
	name = getDbName(name, space, isTest)
	if (typeof(showConsole) !== 'boolean') {
		showConsole = isApp
	}
	if (cloud && name && Object.prototype.toString.call(params) === '[object Object]') {
		/**
		 * 第一步，先确定参数whereObj和db、dbcmd
		 * 如果where不存在，先创建where对象。如果where是字符串，表示是jql语法查询
		 * where有可能来自直接外包，素以where中可能有where之外的所有参数。如果存在，将以此为准
		 * whereObj是一个始终存在的对象，来自于params.where或者{}。即使是jql语法，也仍然可以存在
		 */
		const whereObj = params.where && Object.prototype.toString.call(params.where) === '[object Object]' ? params.where : {}
		// 所有where之外的参数。如果参数有变动，该数组也要随之变动。这些字段都不能作为数据库的字段
		const whereKeys = ['doc', 'jqlOr', 'jqlAnd', 'isJql', 'field', 'orderBy', 'pagination', 'getTree', 'getTreePath', 'parentKey', 'remove', 'getCount', 'keysword', 'foreign', 'GeoPoint', 'data', 'isDelete', 'isReturn', 'addType']
		if (params.where) {
			whereKeys.forEach(key => {
				if (params.where[key] || params.where[key] === 0 || params.where[key] === false) {
					params[key] = whereObj[key]
					delete whereObj[key]
				}
			})
		}
		var {
			doc, // 经转化后，doc要么为空，要么是单个_id。多个id的被写入where中了
			where, // 已经用whereObj代替。如果where是字符串，则仍然保留，表示jql语法
			jqlAnd, jqlOr, isJql,
			field, orderBy, pagination, getTree, getTreePath, parentKey, remove, getCount, keysword, foreign, GeoPoint,
			
			/**
			 * isDelete将会对参数有用，remove时不再限定未删除的。
			 * isReturn腾讯云无法使用doc().updateAndReturn()要改用where().updateAndReturn()
			 */
			data, isDelete, isReturn, addType
		} = params
		// doc的处理
		if (doc) {
			if (typeof(doc) === 'string') {
				doc = doc.split(',')
			}
			if (Array.isArray(doc)) {
				whereObj._id = {filterType: 'select', filter: doc}
				doc = ''
			} else {
				doc = ''
			}
		}
		/**
		 * 确立关键对象
		 * db
		 * dbCmd
		 * whereJql：jql语法时并列添加的内容。不含jqlAnd和jqlOr
		 * whereParams：非jql语法时单个字段的筛选条件
		 */
		var db, dbCmd, whereJql, whereParams, logArr = []
		// 暂时不确定getTree, getTreePath是否需要添加进来。到时候测试一遍
		if (where === 'all') {
			// 表示查询所有。一般仅删除操作清空数据库时用到
			if (isApp) {
				db = cloud.database()
			} else {
				db = cloud.database({timeout})
			}
			dbCmd = db.command
			whereParams = {
				_id: dbCmd.exists(true).or(dbCmd.exists(false)).or(dbCmd.eq(''))
			}
		} else if (isJql || typeof(where) === 'string' || jqlAnd || jqlOr || GeoPoint || getTree || getTreePath) {
			if (isApp) {
				db = cloud.database()
			} else {
				db = cloud.databaseForJQL({timeout})
			}
			dbCmd = db.command
			whereJql = []
			if (typeof(where) === 'string') {
				logArr.push(`jql查询：${where}`)
				whereJql.push(where)
			}
		} else {
			if (isApp) {
				db = cloud.database()
			} else {
				db = cloud.database({timeout})
			}
			dbCmd = db.command
			whereParams = {}
		}
		cloudObj.db = db
		
		/**
		 * 第二步，确定地理查询条件。和where的优先级一样
		 */
		var geoNear
		if (GeoPoint) {
			const {
				key, // 对应计算数据库的字符串
				position, // 必填。字符串。当前要计算的地点的经纬度值。纬度在前，经度在后
				includeLocs, // 字符串。列出要用于距离计算的字段，如果记录中有多个字段都是地理位置时有用
				maxDistance, // 数值。距离最大值
				minDistance, // 数值。距离最小值
				distanceField, // 必填。字符串。存放距离的输出字段名，可以用点表示法表示一个嵌套字段
				distanceMultiplier // 数值。返回时在距离上乘以该数字
			} = GeoPoint
			// jql下的数据库才生效
			const geometry = getGeoPoint(position, db)
			if (geometry) {
				logArr.push(`地理位置: ${position}；${minDistance ? `最小距离：${minDistance}米；` : ''}${maxDistance ? `最大距离：${maxDistance}米；` : ''}`)
				geoNear = (query) => {
					return {
						key, includeLocs, maxDistance, minDistance, distanceField, distanceMultiplier, 
						spherical: true, // 必填，值为 true
						near: geometry, // GeoJSON Point，用于判断距离的点
						query, // 要求记录必须同时满足该条件（语法同 where）
					}
				}
			}
		}
		
		/**
		 * 确立其它查询参数
		 * whereObj中的每个字段都是独立的，且都是数据库的字段名
		 * 特殊类型：
		 *  exist：查询该对象的值存在或者不存在
		 *  search：该字段正则匹配
		 *  select：该字段包含语法
		 * 	defuse：该字段的排除语法，与select相反
		 * 	daterange：按时间范围筛选。该字段本身就是时间范围，要筛选该时间范围是否在要提供的时间范围内
		 * 	date：按时间筛选
		 * 	range：按范围筛选
		 */
		Object.keys(whereObj).forEach(key => {
			var value = whereObj[key]
			if (value && value.filterType) { // 查询该字段存在
				var {
					filterType, // 特殊类型。该值必须存在，都是围绕类型展开的
					filter, // 值
					lte, // 小于时是否包含等于。默认仅小于<
					gte, // 大于时是否包含等于。默认仅大于＞
					other, // 其它表述。在具体类型里意义不一样
				} = value
				switch (filterType){
					/**
					 * 查询该字段存在或者不存在
					 * filter：表示存在或者不存在。默认的不存在还包含了'',[]，不包含false
					 */
					case 'exist': 
						if (filter) {
							if (Array.isArray(filter)) {
								// 要么不存在，要么存在包含filter里面的数值。适用于不存在时表示所有都适用
								if (whereJql) {
									const tempArr = [...jqlObj.unexist(key)]
									filter.map(item => {
										tempArr.push(`${key}.indexOf(${jqlObj.handleValue(item)})>-1`)
									})
									jqlObj.handleArray(tempArr)
									whereJql.push(tempArr.join('||'))
								}
								if (whereParams) {
									whereParams[key] = dbCmd.in(filter).or(dbCmd.exists(false)).or(dbCmd.eq('')).or(dbCmd.eq([]))
								}
								logArr.push(`要么不存在${key}，要么存在包含${filter.join('、')}里面`)
							} else { // 只要存在
								if (whereJql) {
									whereJql.push(`${key}!=""||${key}!=null`)
								}
								if (whereParams) {
									whereParams[key] = dbCmd.exists(true)
								}
								logArr.push(`只要${key}存在`)
							}
						} else { // 只要不存在或者明确为false
							if (whereJql) {
								whereJql.push(jqlObj.unexist.join('||'))
							}
							if (whereParams) {
								whereParams[key] = dbCmd.exists(false).or(dbCmd.eq(false)).or(dbCmd.eq('')).or(dbCmd.eq([]))
							}
							logArr.push(`只要${key}不存在或者为否定状态`)
						}
						break
					/**
					 * 该字段的正则匹配
					 */
					case 'search':
						if (filter) {
							if (whereJql) {
								whereJql.push(`${key}.indexOf(${jqlObj.handleValue(filter)})>-1`)
							}
							if (whereParams) {
								// 使用时jql语法时要用'i'；使用非jql语法时要用'g'？
								whereParams[key] = new RegExp(filter, 'i')
							}
							logArr.push(`${key}搜索：${filter}`)
						}
						break
					/**
					 * 包含语法
					 * 已排除了空数组的情况。但没有排除数组中的空字符串
					 */
					case 'in':
					case 'select':
						if (filter) {
							if (typeof(filter) === 'string') {
								filter = filter.split(',')
							}
							if (Array.isArray(filter)) {
								for (let i = filter.length - 1; i >= 0; i--) {
									if (filter[i] || filter[i] === false || filter[i] === 0) {
										// 存在
									} else { // 不存在的项要去除
										delete filter[i]
									}
								}
								if (filter.length > 0) {
									if (whereJql) {
										const tempArr = []
										filter.map(item => {
											tempArr.push(`${key}==${jqlObj.handleValue(item)}`)
										})
										jqlObj.handleArray(tempArr)
										whereJql.push(tempArr.join('||'))
									}
									if (whereParams) {
										whereParams[key] = dbCmd.in(filter)
									}
									logArr.push(`${key}包含：${filter.join('、')}`)
								}
							}
						}
						break
					/**
					 * 排除语法
					 * 已排除了空数组的情况。但没有排除数组中的空字符串
					 */
					case 'nin':
					case 'defuse':
						if (filter) {
							if (typeof(filter) === 'string') {
								filter = filter.split(',')
							}
							if (Array.isArray(filter) && filter.length > 0) {
								if (whereJql) {
									const arr = []
									filter.map(item => {
										arr.push(`${key}!=${jqlObj.handleValue(item)}`)
									})
									whereJql.push(arr.join('&&'))
								}
								if (whereParams) {
									whereParams[key] = dbCmd.nin(filter)
								}
								logArr.push(`${key}排除：${filter.join('、')}`)
							}
						}
						break
					case 'gt': // 大于
					case 'gte': // 大于等于
					case 'lt': // 小于
					case 'lte': // 小于等于
					case 'neq': // 不等于
					case 'eq': // 等于
						if (filter) {
							var symbol, text
							switch (filterType){
								case 'gt':
									symbol = '>'
									text = '大于'
									break
								case 'gte':
									symbol = '>='
									text = '大于等于'
									break
								case 'lt':
									symbol = '<'
									text = '小于'
									break
								case 'lte':
									symbol = '<='
									text = '小于等于'
									break
								case 'neq':	
									symbol = '!='
									text = '不等于'
									break
								case 'eq':
									symbol = '=='
									text = '等于'
									break
								default:
									break
							}
							if (whereJql) {
								whereJql.push(`${key}${symbol}${typeof(filter) === 'string' ? `'${filter}'` : filter}`)
							}
							if (whereParams) {
								whereParams[key] = dbCmd[filterType](filter)
							}
							logArr.push(`${key} ${text} ${filter}`)
						}
						break
					/**
					 * 按范围的筛选
					 * filter：是目的值域
					 * 独立元素是开始值和结束值组成的数组。时间范围也可以是单个，表示在字段时间范围内的一个时间；其它如果是单个，表示小于或等于该值，要强制转化为数组
					 * 可以由多个这样的独立元素组成的复杂数组。也可以是单个独立元素组成的简单数组。
					 * 第一步先把简单数组转化为复杂数组
					 * 
					 * 时间范围有两种情况。开始时间或者结束时间，只要一个在范围内和两者都在范围内；
					 * other：默认false，表示只要一个在范围内；true，表示需要两者都在范围内
					 * 
					 * 时间和数值没有other
					 */
					case 'daterange':
					/**
					 * 按时间筛选
					 */
					case 'date':
					/**
					 * 按范围筛选
					 */
					case 'range':
						// 要求必须是独立元素，两个长度的数组或者单个元素
						if (filter) {
							if (Array.isArray(filter)) {
								if (filter.length > 0) {
									if (Array.isArray(filter[0]) && filter[0].length === 2 && !Array.isArray(filter[0][0])) {
										// 简单数组转化为复杂数组
										filter = [filter]
									}
								}
							} else {
								if (filterType === 'daterange') {
									// 该值落在数据时间范围内
									filter = [filter]
								} else {
									// 单个值变为最大值
									filter = [[undefined, filter]]
								}
							}
							// 已经转化为复杂数组。复杂数组内各项是或的关系
							const tempArr = [], // jql语句的情况
								  // and数组表示都要满足，or数组表示满足任意一项即可。
								  keyOr = [], // date或者range的值情况
								  key0And = [], key0Or = [], // daterange的第一个值的情况
								  key1And = [], key1Or = [], // daterange的第二个值的情况
								  tempLog = [] // 记录要打印的文本
							filter.map(item => { // item须是独立元素
								if (item > 0 && filterType === 'daterange') {
									// 该值在数据范围内即可
									if (whereJql) {
										var str1 = `${key}.0${lte ? '<=' : '<'}${item}`
										var str2 = `${key}.1${gte ? '>=' : '>'}${item}`
										tempArr.push(`(${str1})&&(${str2})`)
									}
									if (whereParams) {
										var str1 = dbCmd[`${lte ? 'lte' : 'lt'}`](item)
										var str2 = dbCmd[`${gte ? 'gte' : 'gt'}`](item)
										key0And.push(str1)
										key1And.push(str2)
									}
									tempLog.push('单个值：'+ item)
								} else if (Array.isArray(item) && item.length === 2 && (item[0] === undefined || item[0] < item[1])) {
									// 开始和结束值在这个范围都可
									if (whereJql) {
										var str1 = `${key}${filterType === 'daterange' ? '.0' : ''}${lte ? '<=' : '<'}${item[1]}`
										var str2 = `${key}${filterType === 'daterange' ? '.1' : ''}${gte ? '>=' : '>'}${item[0]}`
										if (filterType === 'daterange') {
											if (other) { // 两者都要在
												tempArr.push(`(${str1})&&(${str2})`)
												tempLog.push(`起止值都在：${item[0]}~${item[1]}`)
											} else { // 只要有一方在
												tempArr.push(`(${str1})||(${str2})`)
												tempLog.push(`起止值有在：${item[0]}~${item[1]}`)
											}
										} else {
											// 数据值在范围内
											tempArr.push(`(${str1})&&(${str2})`)
											tempLog.push(`${item[0]}~${item[1]}`)
										}
									}
									if (whereParams) {
										var str1 = dbCmd[`${lte ? 'lte' : 'lt'}`](item[1])
										var str2 = dbCmd[`${gte ? 'gte' : 'gt'}`](item[0])
										if (filterType === 'daterange') {
											if (other) { // 两者都要在
												key0And.push(str1)
												key1And.push(str2)
												tempLog.push(`起止值都在：${item[0]}~${item[1]}`)
											} else { // 只要有一方在
												key0Or.push(str1)
												key1Or.push(str2)
												tempLog.push(`起止值有在：${item[0]}~${item[1]}`)
											}
										} else {
											keyOr.push(str1.and(str2))
											tempLog.push(`${item[0]}~${item[1]}`)
										}
									}
								}
							})
							if (tempArr.length > 0 || (keyOr.length > 0 || key0And.length > 0 || key0Or.length > 0 || key1And.length > 0 || key1Or.length > 0)) {
								if (whereJql) {
									jqlObj.handleArray(tempArr)
									whereJql.push(tempArr.join('||'))
								}
								if (whereParams) {
									const getDbCmd = (dbAnd, dbOr) => {
										var keyObj
										dbAnd && dbAnd.map(obj => {
											if (keyObj) {
												keyObj = (keyObj).and(obj)
											} else {
												keyObj = obj
											}
										})
										dbOr && dbOr.map(obj => {
											if (keyObj) {
												keyObj = (keyObj).or(obj)
											} else {
												keyObj = obj
											}
										})
										return keyObj
									}
									if (filterType === 'daterange') {
										whereParams[`${key}.0`] = getDbCmd(key0And, key0Or)
										whereParams[`${key}.1`] = getDbCmd(key1And, key1Or)
									} else {
										whereParams[key] = getDbCmd(undefined, keyOr)
									}
								}
								logArr.push(`${key}的筛选范围：${tempLog.join('，')}`)
							}
						}
						break
					default:
						break
				}
			} else {
				if (whereJql) {
					whereJql.push(`${key}==${jqlObj.handleValue(value)}`)
				}
				if (whereParams) {
					whereParams[key] = value
				}
				logArr.push(`${key}：${value}`)
			}
		})
		// 多字段搜索
		if (keysword) {
			const {keys, word} = keysword
			if (keys && word) {
				const arr = Array.isArray(keys) ? keys : keys.split(',')
				const tempArr = [], orArr = []
				arr.map(key => {
					var str = new RegExp(word, 'i')
					tempArr.push(`${str}.test(${key})`)
					
					var obj = {}
					obj[key] = str
					orArr.push(obj)
				})
				if (whereJql) {
					jqlObj.handleArray(tempArr)
					whereJql.push(tempArr.join('||'))
				}
				if (whereParams) {
					if (!whereParams.orArr) {
						whereParams.orArr = []
					}
					// 最后再处理的or语法
					whereParams.orArr.push(orArr)
				}
				logArr.push(`在${arr.join('、')}中搜索：${word}`)
			}
		}
		// 删除字段内容查询
		if (isDelete) {
			// 真的删除，将查询所有
		} else {
			// 查询类的，一定要添加删除字段查询。非查询类的不需要 - 否则无论怎么样都能查到内容了
			if (getBack || remove) {
				logArr.push(`remove：${remove || 0}`)
				switch (remove){
					case 1: // 这是查询完整的全部。默认要有remove字段
						break
					case 2: // 这是查一般性删除的内容。默认要有remove字段
						if (whereJql) {
							whereJql.push('remove==true')
						}
						if (whereParams) {
							whereParams.remove = dbCmd.eq(true)
						}
						break
					case 0: // 不查一般性删除的内容，这是正常查询。默认没有remove字段
					default:
						if (whereJql) {
							whereJql.push('remove!=true')
						}
						if (whereParams) {
							whereParams.remove = dbCmd.neq(true)
						}
						break
				}
			}
		}
		// 补充查询内容
		if (jqlAnd) {
			if (Array.isArray(jqlAnd)) {
				jqlObj.handleArray(jqlAnd)
				// 内部都是与。如果要或，请在具体语句中写好
				jqlAnd = jqlAnd.join('&&')
			}
			whereJql.push(jqlAnd)
			logArr.push(`其余并列查询：${jqlAnd}`)
		}
		// jql的所有的参数展开
		if (whereJql) {
			jqlObj.handleArray(whereJql)
			whereJql = whereJql.join('&&')
			if (jqlOr) {
				if (Array.isArray(jqlOr)) {
					jqlObj.handleArray(jqlOr)
					// 内部都是或。如果要与，请在具体语句中写好
					jqlOr = jqlOr.join('||')
				}
				whereJql = `(${whereJql})||(${jqlOr})`
				logArr.push(`其余与或查询：${jqlOr}`)
			}
		}
		
		if (whereJql || whereParams && Object.keys(whereParams).length > 0 || remove === 1) {
			/**
			 * 第三步，确立查询对象
			 */
			db = db.collection(name)
			if (geoNear) {
				db = db.geoNear(geoNear(whereJql))
			} else {
				if (whereJql) {
					db = db.where(whereJql)
				} else if (whereParams) {
					if (whereParams.orArr) {
						var obj = dbCmd.or(...whereParams.orArr[0])
						for (let i = 1; i < whereParams.orArr.length; i++) {
							obj = obj.or(...whereParams.orArr[i])
						}
						delete whereParams.orArr
						Object.keys(whereParams).forEach(key => {
							const temp = {}
							temp[key] = whereParams[key]
							obj = obj.and(temp)
						})
						db = db.where(obj)
					} else {
						db = db.where(whereParams)
					}
				}
			}
			
			/**
			 * 第四步确立其它参数 - 一般只有在get查询条件下才执行
			 * 优先级：
			 * field
			 * orderBy
			 * parentKey
			 * skip：pageSize * pageIndex
			 * limit：pageSize
			 */
			/**
			 * 正常情况下，要排除掉HISTORY和remove字段
			 * 但是阿里云和支付宝云不支持排除语法，所以注释掉！
			 * 统一有配置信息的defuseKeys控制，将在返回数据时去除
			 * 
			 * 阿里云后端明确不支持field('字符串')，特地将字符串形式全部转为obj
			 */
			if (field) {
				if (Array.isArray(field)) {
					field = field.join(',')
				}
				/**
				 * 返回字段
				 * 
				 * 支持对象：{ 'age': true }
				 * 仅jqp支持这种字段变化：code as value, name as text
				 */
				if (typeof(field) === 'string') {
					if (field.indexOf(' as ') > -1) {
						if (!whereJql) {
							const obj = {}
							const arr = field.split(',')
							arr.map(str => {
								if (str.indexOf('as') > -1) {
									// 去除空格
									const key = str.split('as')[0].replace(/\s+|-/g, "")
									obj[key] = true
								} else {
									// 去除空格
									const key = str.replace(/\s+|-/g, "")
									obj[key] = true
								}
							})
							db = db.field(obj)
						}
					} else {
						if (isApp) {
							db = db.field(field)
						} else {
							const obj = {}
							field.split(',').map(str => {
								// 去除空格
								const key = str.replace(/\s+|-/g, "")
								obj[key] = true
							})
							db = db.field(obj)
						}
					}
					logArr.push('返回字段：' + field)
				} else {
					db = db.field(field)
					logArr.push('返回字段：' + JSON.stringify(field))
				}
			}
			if (getBack) {
				if (!orderBy) {
					// 默认按时间降序
					orderBy = 'timestamp desc'
				}
				if (orderBy === '无') {
					// 明确不排序，才不排序
				} else {
					if (whereJql) {
						db = db.orderBy(orderBy)
					} else {
						const arr = Array.isArray(orderBy) ? orderBy : orderBy.split(',')
						for (let i = 0; i < arr.length; i++) {
							if (arr[i].length > 0) {
								if (arr[i][0] === ' ') {
									// 去除前面的空格
									arr[i] = arr[i].substr(1)
								}
								// 用中间的空格判断数组
								const a = arr[i].split(' ')
								if (a.length === 1) {
									// asc升序，可以省略asc
									db = db.orderBy(a[0])
								} else if (a.length === 2) {
									db = db.orderBy(a[0], a[1])
								}
							}
						}
					}
					logArr.push('排序方式：' + orderBy)
				}
				if (parentKey) {
					// 指定父节点查询字段
					db = db.parentKey(parentKey)
					logArr.push('查询父节点：' + parentKey)
				}
				if (pagination) {
					const {pageIndex, pageSize} = pagination
					if (pageIndex >= 0 || pageSize > 0) {
						// 按页码查询，则一定要查询数量
						logArr.push(`当前第${pageIndex + 1}页，每页容量${pageSize}个`)
						db = db.skip(pageSize * pageIndex) // 跳过之前的查询数量
							   .limit(pageSize) // 获取当前页码尺寸的数量
					} else {
						pagination = undefined
					}
				}
				// 查询类型时，要返回必须额外处理的内容
				if (typeof(getBack) === 'function') {
					getBack({
						getTree, getTreePath, getCount, // 请求时处理
						foreign, pagination // 请求完成后，拿到了数据，再做联表查询。并且对页码做梳理
					})
				}
			}
			if (showConsole) {
				if (logArr.length > 0) {
					console.log(`查询参数表述以下${logArr.length}行：`)
					logArr.map((str, i) => {
						console.log(`${i + 1}：${str}${i < logArr.length - 1 ? '；' : '。'}`)
					})
				} else {
					console.log(`查询参数：无` )
				}
			}
			return db
		} else {
			// 没有可查询的内容，不查询。否则查到所有。在删除和修改的场景下，会出大问题
		}
	}
	return undefined
}